package org.zjvis.datascience.service;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.api.client.util.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zjvis.datascience.common.constant.IdConstant;
import org.zjvis.datascience.common.constant.NoticeConstant;
import org.zjvis.datascience.common.constant.ProjectConstant;
import org.zjvis.datascience.common.dto.*;
import org.zjvis.datascience.common.dto.user.UserDTO;
import org.zjvis.datascience.common.enums.NoticeTypeEnum;
import org.zjvis.datascience.common.enums.ProjectRoleAuthEnum;
import org.zjvis.datascience.common.enums.ProjectUserTypeEnum;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.model.TaskItems;
import org.zjvis.datascience.common.util.*;
import org.zjvis.datascience.common.vo.project.*;
import org.zjvis.datascience.common.widget.dto.WidgetDTO;
import org.zjvis.datascience.service.mapper.PipelineMapper;
import org.zjvis.datascience.service.mapper.ProjectMapper;
import org.zjvis.datascience.service.mapper.UserProjectMapper;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @description Project 项目 Service
 * @date 2021-10-18
 */
@Service
public class ProjectService {

    private final static Logger logger = LoggerFactory.getLogger("ProjectService");

    @Lazy
    @Autowired
    private TaskService taskService;

    @Lazy
    @Autowired
    private NoticeService noticeService;

    @Lazy
    @Autowired
    private DatasetProjectService datasetProjectService;

    @Lazy
    @Autowired
    private DashboardService dashboardService;

    @Lazy
    @Autowired
    private UserProjectService userProjectService;

    @Autowired
    private WidgetService widgetService;

    @Autowired
    private JlabService jlabService;

    @Lazy
    @Autowired
    private PanelService panelService;

    @Autowired
    private UserProjectMapper userProjectMapper;

    @Autowired
    private ProjectMapper projectMapper;

    @Autowired
    private PipelineMapper pipelineMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private TaskUtil taskUtil;


    public ProjectDTO query(long id) {
        return projectMapper.selectByPrimaryKey(id);
    }


    public boolean exist(long id) {
        return projectMapper.selectByPrimaryKey(id) != null;
    }

    public long save(ProjectDTO dto) {
        try {
            projectMapper.insertSelective(dto);
        } catch (DuplicateKeyException e) {
            throw new DataScienceException(BaseErrorCode.PROJECT_NAME_DUPLICATE_ERROR);
        }
        return dto.getId();
    }

    public int delete(long id) {
        return projectMapper.deleteByPrimaryKey(id);
    }

    public int update(ProjectDTO dto) {
        Preconditions.checkNotBlankIfNotNull(dto.getName(), ProjectVO.NAME_NOT_BLANK);
        Preconditions.checkSizeIfNotNull(dto.getName(), 1, 50, ProjectVO.NAME_SIZE);
        Preconditions.checkSizeIfNotNull(dto.getDescription(), 0, 100, ProjectVO.DESC_SIZE);
        int res = projectMapper.updateByPrimaryKeySelective(dto);
        if (res > 0) {
            String title = NoticeConstant.PROJECT_MESSAGE_CHANGE_TITLE;
            String content = NoticeConstant.PROJECT_MESSAGE_CHANGE_CONTENT;
            sendNotice(dto.getId(), title, content);
        }
        return res;
    }

    /**
     * 清空project下的pipeline，目前一个project下面只有一个pipeline
     *
     * @param projectId
     * @return
     */
    public boolean clearProject(Long projectId) {
        boolean isSuccess = true;
        Long pipelineId = null;
        List<Long> taskIds = taskService.queryIdByProjectId(projectId);
        for (Long id : taskIds) {
            if (pipelineId == null) {
                TaskDTO dto = taskService.queryById(id);
                pipelineId = dto.getPipelineId();
            }
            try {
                taskService.delete(id);
            } catch (Exception e) {
                isSuccess = false;
                break;
            }
        }
        taskService.clearTaskUtil(pipelineId);
        return isSuccess;
    }

    public Object page(ProjectPageVO ppv) {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        List<Long> projectIds = new ArrayList<>();

        String orderBy = ppv.getSortColumn() + " " + ppv.getSortMethod();
        if (1 == ppv.getProjectType()) {
            if (StringUtils.isBlank(ppv.getSortColumn())) {
                PageHelper.startPage(ppv.getPageNum(), ppv.getPageSize());
            } else {
                PageHelper.startPage(ppv.getPageNum(), ppv.getPageSize(), orderBy);
            }

            ProjectDTO pv = new ProjectDTO();
            pv.setType(1);
            List<ProjectDTO> list = projectMapper.page(pv);
            return PageUtils.getPageResult(PageInfo.of(list));
        } else if (2 == ppv.getProjectType()) {
            //获取自己参与项目的id
            projectIds = userProjectMapper.listUserProjectIds(user.getId(),
                    ProjectUserTypeEnum.partaker.getType());
        } else {
            //获取获取本人创建的项目id
            projectIds = userProjectMapper.listUserProjectIds(user.getId(),
                    ProjectUserTypeEnum.creator.getType());
        }

        if (projectIds.isEmpty()) {
            return projectIds;
        }
        if (StringUtils.isBlank(ppv.getSortColumn())) {
            PageHelper.startPage(ppv.getPageNum(), ppv.getPageSize());
        } else {
            PageHelper.startPage(ppv.getPageNum(), ppv.getPageSize(), orderBy);
        }

        List<ProjectDTO> pjs = projectMapper.list(projectIds);
        return PageUtils.getPageResult(PageInfo.of(pjs));
    }

    public void checkLockAuth(Long projectId) {

    }

    public ProjectStatusDTO getProjectStatus(Long projectId) {
        return projectMapper.getProjectStatus(projectId);
    }

    public void changeStatus(ProjectDTO project) {
        int res = projectMapper.updateByPrimaryKeySelective(project);
        if (res > 0) {
            String title = project.getLock() == 0 ? NoticeConstant.PROJECT_UNLOCK_TITLE
                    : NoticeConstant.PROJECT_LOCK_TITLE;
            String content = project.getLock() == 0 ? NoticeConstant.PROJECT_UNLOCK_CONTENT
                    : NoticeConstant.PROJECT_LOCK_CONTENT;
            sendNotice(project.getId(), title, content);
            //前置修改成功，删除缓存中的状态值
            redisUtil.del(String.format(ProjectConstant.PROJECT_STATUS_KEY, project.getId()));
        }
    }

    public List<ProjectDTO> queryByIds(Set<Long> ids) {
        return projectMapper.queryByIds(ids);
    }

    public List<ProjectNameDTO> queryProjectName(long userId) {
        //获取和指定用户有关的所有项目id
        List<Long> projectIds = userProjectMapper.listUserProjectIds(userId, null);
        if (projectIds == null || projectIds.isEmpty()) {
            return null;
        }
        List<ProjectDTO> pjs = projectMapper.list(projectIds);
        List<ProjectNameDTO> res = new ArrayList<>();
        for (ProjectDTO project : pjs) {
            res.add(DozerUtil.mapper(project, ProjectNameDTO.class));
        }
        return res;
    }

    private void sendNotice(Long projectId, String title, String content) {
        if (projectId == null || StringUtils.isBlank(title) || StringUtils.isBlank(content)) {
            return;
        }

        try {
            Set<Long> receivers = userProjectService.getReceiver(projectId,
                    new HashSet<Long>() {{
                        add(JwtUtil.getCurrentUserId());
                    }},
                    new HashSet<Integer>() {{
                        add(ProjectRoleAuthEnum.VISITOR.getValue());
                    }});
            ProjectDTO res = projectMapper.selectByPrimaryKey(projectId);
            if (res == null) {
                return;
            }
            String projectName = res.getName();
            content = String.format(content,
                    StringUtil.addHtmlBoldLabel(projectName),
                    StringUtil.addHtmlBoldLabel(JwtUtil.getCurrentUserDTO().getName()));
            JSONObject process = new JSONObject();
            process.put("projectId", projectId);
            noticeService.saveAndSendToUser(receivers, title, content, process.toJSONString(),
                    NoticeTypeEnum.project.getType(), JwtUtil.getCurrentUserId());
        } catch (Exception e) {
            logger.error("project操作相关，发送消息失败", e);
        }
    }

    /**
     * 批量复制
     *
     * @param newProjectId
     * @param oldProjectId
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchCopy(long newProjectId, Long oldProjectId, Boolean needForce) {
        Map<Long, Long> relationMap = null;
        //1.复制权限 (暂不需要)
        //2.复制数据源信息
        List<ProjectDataSourceVO> dataSourceVOS = datasetProjectService
                .queryProjectDataset(new ProjectDatasetVO(oldProjectId, null, null));
        List<TaskDTO> oldTaskDTOS = new ArrayList<>();
        Long oldPipelineId = pipelineMapper.queryByProject(oldProjectId).get(0).getId();
        Long newPipelineId = pipelineMapper.getDefaultPipelineId(newProjectId);
        if (dataSourceVOS.size() > 0) {
            List<ProjectDataSourceTableVO> datasets = dataSourceVOS.get(0).getDataset();
            Set<Long> datasetIds = datasets.stream().map(ProjectDataSourceTableVO::getId)
                    .collect(Collectors.toSet());
            datasetProjectService.add(new LoadDatasetVO(datasetIds, newProjectId), needForce);
        }
        //3.复制Task节点
        oldTaskDTOS = taskService.queryByPipeline(oldPipelineId);
        if (oldTaskDTOS.size() != 0) {
            oldTaskDTOS.stream().forEach(task -> {
                task.setPipelineId(newPipelineId);
                task.setProjectId(newProjectId);
                task.setGmtCreator(JwtUtil.getCurrentUserId());
                task.setUserId(JwtUtil.getCurrentUserId());
            });
            if (!taskService.batchProduce(oldTaskDTOS, oldPipelineId, true)) {
                throw DataScienceException
                        .of(BaseErrorCode.PROJECT_COPY_FAILED, null, "复制节点过程中失败。");
            }
            relationMap = taskUtil.getRelationShipByPipelineId(oldPipelineId);
            relationMap.put(IdConstant.PIPELINE_DUMMY_IDX, newPipelineId); //新生成的pipelineId 也添加进去
        }
        //4.复制快照 (暂时先不复制)
//            pipelineSnapshotService.queryByPipeline(oldPipelineId);
        //8. 复制自定义算子（JupyterLab）中的内容
        ProjectDTO newProject = this.query(newProjectId);
        ProjectDTO oldProject = this.query(oldProjectId);
        jlabService.copyPasteProjectDir(newProject.getGmtModifier(),oldProject.getName(),newProject.getName());

        //5. 复制可视化视图中的内容 (复制出来默认不发布)
        DashboardDTO oldDashboard = dashboardService.queryByProjectId(oldProjectId);
        if (null != oldDashboard) {
            dashboardService.copy(oldDashboard.getId(), newProjectId, relationMap);
        } else {
            //虽然没有可视化构建，但是可能会有 可视化收藏的一些内容，也需要复制
            List<Long> taskIds = widgetService.getAllWidgetIdsByDFS(oldPipelineId);
            if (taskIds != null && taskIds.size()> 0) {
                List<WidgetDTO> widgetDTOS = widgetService.queryByTaskIds(taskIds);
                widgetService.batchInsert(widgetDTOS.stream().map(WidgetDTO::getId).collect(Collectors.toList()), relationMap);
            }
            //还是没有
        }
        //6. 复制图分析中的内容
        //7. 复制我的模型中的内容
        TaskItems folder = panelService.loadMLModel(new PanelDTO(), new ProjectNameVO(oldProjectId)).getPanel().get(0);
        panelService.batchCopy(folder, newProjectId);
    }

    /**
     * 查看
     *
     * @param projectId
     * @param userId
     * @return
     */
    public int countExistPipeline(long projectId, Long userId) {
        return pipelineMapper.countPipelineInSpecificProject(projectId, userId);
    }

    /**
     * 获取这个project 默认的pipelineId
     *
     * @param projectId
     * @return
     */
    public Long getDefaultPipelineId(long projectId) {
        ProjectDTO projectDTO = this.query(projectId);
        long currentUserId = JwtUtil.getCurrentUserId();
        List<PipelineDTO> pipelineDTOS = Lists.newArrayList();
//        if (currentUserId == 0L) {
//            //TODO 未登录状态，可视化构建可能会触发 暂时先去掉
//            return null;
//        }
        if (projectDTO.getGmtCreator().equals(currentUserId)) {
            pipelineDTOS = pipelineMapper.queryByProjectAndUser(projectId, JwtUtil.getCurrentUserId());
        } else {
            pipelineDTOS = pipelineMapper.queryByProjectAndUser(projectId, 0L);
        }
        return pipelineDTOS.stream().map(pipelineDTO -> {
            JSONObject json = JSONObject.parseObject(pipelineDTO.getDataJson());
            if (!json.containsKey("published")) {
                return pipelineDTO.getId();
            }
            return null;
        }).findFirst().get();
    }

}
